package com.sam.lt.service;

import java.util.List;

import javax.annotation.Resource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import com.sam.lt.dao.NodeRepository;
import com.sam.lt.entity.Node;
import com.sam.lt.vo.TreeNodeVo;

@Service
public class NodeServiceImpl implements NodeService {
	private final Logger logger = LogManager.getLogger(this.getClass());
	
	@Resource
	NodeRepository nodeRepository;
	
	@Override
	public Node createNode(String value,Integer parent) {
		Node node = new Node();
		node.setValue(value);
		node.setParentId(parent);
		Node res = nodeRepository.save(node);
		return res;
	}
	
	public TreeNodeVo viewTree(Integer NodeId,Integer level) {
		int levelCounter = 0;
		Node node = nodeRepository.getById(NodeId);
		if(node != null) {
			TreeNodeVo vo = TreeNodeVo.fromEntity(node);
			connectChildrenNode(vo,(levelCounter++),level);
			
			return vo;
		}
		
		return null;
	}

	@Override
	public TreeNodeVo viewTree(Integer NodeId) {
		Node node = nodeRepository.getById(NodeId);
		if(node != null) {
			TreeNodeVo vo = TreeNodeVo.fromEntity(node);
			connectChildrenNode(vo,0,999);
			return vo;
		}
		
		return null;
	}
	
	private void connectChildrenNode(TreeNodeVo vo,int curLevel,int requirelevel) {
		if(curLevel >= requirelevel)
			return ;
		List<TreeNodeVo> children = getNodeChildren(vo.getId());
		if(children != null) {
			vo.addChild(children);
			for(TreeNodeVo childVo : children) {
				connectChildrenNode(childVo,(curLevel++),requirelevel);
			}
		}
	}
	
	private List<TreeNodeVo> getNodeChildren(Integer NodeId) {
		Node probe = new Node();
		probe.setParentId(NodeId);
		Example<Node> exaple = Example.of(probe);
		
		Sort sort = Sort.by(Sort.Direction.ASC,"id");
		List<Node> list = nodeRepository.findAll(exaple, sort);
		return TreeNodeVo.fromEntity(list);
	}
	
	private void removeNodeAndChild(Integer NodeId) {
		List<TreeNodeVo> children = getNodeChildren(NodeId);
		if(children != null) {
			for(TreeNodeVo childVo : children) {
				removeNodeAndChild(childVo.getId());
			}
		}
		
		nodeRepository.deleteById(NodeId);
	}
	
	private void updateBranch(Integer parentId,TreeNodeVo root) {
		boolean exist = nodeRepository.existsById(root.getId());
		
		if(!exist) { //node not exist,then save to tree
			insertNodeAndChild(parentId,root);
		}
		else {
			Node node = nodeRepository.getById(root.getId());
			node.setParentId(parentId);
			node.setValue(root.getValue());
			nodeRepository.save(node);
		}
		
		List<TreeNodeVo> children = root.getChildren();
		if(children != null) {
			for(TreeNodeVo childVo : children) {
				updateBranch(root.getId(),childVo);
			}
		}
	}
	
	private void insertNodeAndChild(Integer parentId,TreeNodeVo root) {
		Node node = root.cloneToBeNode();
		node.setParentId(parentId);
		Node res = nodeRepository.save(node);
		if(root.getChildren() != null) {
			for(TreeNodeVo childVo : root.getChildren()) {
				insertNodeAndChild(res.getId(),childVo);
			}
		}
	}

	@Override
	public void removeNode(Integer NodeId) {
		removeNodeAndChild(NodeId);
	}

	@Override
	public TreeNodeVo getNode(Integer NodeId) {
		Node node = nodeRepository.getById(NodeId);
		return TreeNodeVo.fromEntity(node);
	}

	@Override
	public List<TreeNodeVo> ListAllTree() {
		Node probe = new Node();
		probe.setParentId(0);
		Example<Node> exaple = Example.of(probe);
		
		Sort sort = Sort.by(Sort.Direction.ASC,"id");
		List<Node> list = nodeRepository.findAll(exaple, sort);
		return TreeNodeVo.fromEntity(list);
	}

	@Override
	public TreeNodeVo getTreeByName(String name) {
		return getTreeByName(name,0);
	}
	
	@Override
	public TreeNodeVo getTreeByName(String name, Integer level) {
		Node probe = new Node();
		probe.setValue(name);
		probe.setParentId(0);
		Example<Node> exaple = Example.of(probe);
		
		Sort sort = Sort.by(Sort.Direction.ASC,"id");
		List<Node> list = nodeRepository.findAll(exaple, sort);
		if(list.size() > 0)
			return viewTree(list.get(0).getId());
		return null;
	}
	
	/**
	 * root ,must be tree root node
	 */
	@Override
	public void updateOrCreateTree(TreeNodeVo root) {
		if(nodeRepository.existsById(root.getId())) {
			logger.info("Refresh tree:{}",root.getValue());
			updateBranch(0,root);
		}
		else {
			logger.info("Create new tree:{}",root.getValue());
			insertNodeAndChild(0,root);
		}
	}
}
